class Snake {
    constructor() {
        this.step = 30 // 步进大小(缩放大小)
        this.initLen = 3 // 初始身体长度
        this.initData()
    }
    // 初始化游戏数据
    initData() {
        this.id = 'snakeid' + Date.now()
        this.fraction = 0 // 分数
        this.historyFraction = localStorage.getItem('historyFraction') // 历史最高分数
        this.currentDirection = 'd' // 当前方向
        this.autoSet = null // 自动移动定时器
        this.snakeBody = [] // 角色身体
        document.body.innerHTML = '' // 清空页面
        this.mapGame = document.createElement('div');
        this.sceneAdaptation()
        this.placeScene()
        this.createFood()
        this.createSnake()
        this.createUserMsg()
        this.createDialog()
        this.renderBody()
        document.onkeypress = (e) => {
            this.currentDirection = e.key
            this.motion(e.key)
            this.autoMotion()
        }
    }
    // 根据设备适配最佳场景大小
    sceneAdaptation() {
        let w = window.innerWidth
        let h = window.innerHeight
        this.sW = ~~(w / this.step)// 场景宽度
        this.sH = ~~(h / this.step)// 场景高度
    }
    // 放置游戏场景
    placeScene() {
        this.setStyle(this.mapGame, {
            width: this.sW,
            height: this.sH,
            top: '0',
            left: '0',
            position: 'absolute',
            backgroundColor: '#121213',
        })
        document.body.appendChild(this.mapGame);
    }
    // 创建食物
    createFood() {
        let food = document.getElementById('food' + this.id)
        if (food) {
            this.mapGame.removeChild(food)
        }
        this.foodDiv = this.createDom({
            borderRadius: '0',
            top: ~~(Math.random() * this.sH),
            left: ~~(Math.random() * this.sW)
        })
        this.foodDiv.id = 'food' + this.id
        this.mapGame.appendChild(this.foodDiv);
    }
    // 初始创建角色
    createSnake() {
        for (let i = 0; i < this.initLen; i++) {
            let item = {
                type: 'body'
            }
            item.dom = this.createDom({ top: 1, left: i })
            // 头部
            if (i == this.initLen - 1) {
                item.type = 'head'
                this.setStyle(item.dom, { backgroundColor: 'blue', border: '1px solid #F7D43D' })
            }
            this.snakeBody.push(item);
        }
    }
    // 角色移动操作
    motion(key) {
        // 判断弹窗是否打开
        if (this.dialog.style.display === 'block') return
        let len = this.snakeBody.length
        let head = this.snakeBody[len - 1]
        let { top, left } = this.getDomAddr(head.dom)
        const headDirection = {
            w: { top: top - 1, left: left },
            s: { top: top + 1, left: left },
            a: { top: top, left: left - 1 },
            d: { top: top, left: left + 1 },
        }
        // 接下来头要移动到的位置
        let nextArea = headDirection[key]
        // 边界判断
        if (!this.boundary(nextArea)) return
        // 吃到食物判断
        this.growUp(nextArea)
        // 同步身体位置
        this.snakeBody.forEach((item, index) => {
            if (item.type === 'body') {
                this.setStyle(item.dom, this.getDomAddr(this.snakeBody[index + 1].dom));
            }
        })
        // 同步头的位置
        this.setStyle(head.dom, nextArea);
    }
    // 开启自动移动操作
    autoMotion() {
        if (!this.autoSet) {
            let speed = this.fraction > 50 ? this.fraction / 40 : 1
            this.autoSet = setInterval(() => {
                this.motion(this.currentDirection)
            }, 500 / speed)
        } else {
            clearInterval(this.autoSet)
            this.autoSet = null
            this.autoMotion()
        }
    }
    // 关闭自动移动
    closeAutoMotion() {
        if (this.autoSet) {
            clearInterval(this.autoSet)
            this.autoSet = null
        }
    }
    // 判断吃到食物成长
    growUp(nextArea) {
        let { top, left } = this.getDomAddr(this.foodDiv)
        if (nextArea.top == top && nextArea.left == left) {
            let newBody = {
                type: 'body',
                time: Date.now()
            }
            newBody.dom = this.createDom({
                top: this.getDomAddr(this.foodDiv).top,
                left: this.getDomAddr(this.foodDiv).left
            })
            this.snakeBody.unshift(newBody)
            this.statistics()
            this.renderBody()
            this.createFood()
        }
    }
    // 边界判断
    boundary(nextArea) {
        if (nextArea.top < 0 || nextArea.left < 0 || nextArea.top >= this.sH || nextArea.left >= this.sW) {
            this.gameOver()
            return false
        }
        return true
    }
    // 游戏结束操作
    gameOver() {
        this.closeAutoMotion()
        const gradeList = [
            { name: '绝世高手', fraction: 350 },
            { name: '游戏熟手', fraction: 250 },
            { name: '大众水平', fraction: 100 },
            { name: '手残党', fraction: 0 }
        ]
        for (let item of gradeList) {
            if (this.fraction >= item.fraction) {
                this.grade = item.name
                break;
            }
        }
        // 保存游戏分数到缓存
        if (this.fraction > this.historyFraction) {
            let historyFraction = document.getElementById('historyFraction' + this.id)
            historyFraction.innerText = '最高分数：' + this.fraction
            localStorage.setItem('historyFraction', this.fraction)
        }
        this.dialog.style.display = 'block'
        this.dialog.innerHTML = `<h2>GameOver</h2><p>你的段位是：${this.grade} </p><p>分数：${this.fraction} </p><p>点击重新开始</p>`
    }
    // 分数统计
    statistics() {
        this.fraction += ~~(Math.random() * this.snakeBody.length + 1)
    }
    // 创建游戏弹窗
    createDialog() {
        const that = this
        this.dialog = this.createDom({
            width: this.sW * 0.4,
            height: this.sW * 0.2,
            zIndex: '300',
            borderRadius: '20px',
            top: '0',
            left: '0',
            right: '0',
            bottom: '0',
            margin: 'auto',
            textAlign: 'center',
            display: 'none',
            backgroundColor: '#ffffff'
        })
        this.dialog.onclick = function () {
            this.style.display = 'none'
            that.closeAutoMotion()
            that.initData()
        }
        this.mapGame.appendChild(this.dialog)
    }
    // 创建角色信息展示栏
    createUserMsg() {
        let msgStyle = {
            width: 5,
            height: 1,
            zIndex: '200',
            borderRadius: '0',
            textAlign: 'center',
            display: 'inline-block',
            backgroundColor: 'transparent',
            color: '#ffffff'
        }
        let fraction = this.createDom(msgStyle)
        let historyFraction = this.createDom(msgStyle)
        fraction.id = 'fraction' + this.id
        fraction.innerText = '分数：' + this.fraction
        historyFraction.id = 'historyFraction' + this.id
        historyFraction.innerText = '最高分数：' + (this.historyFraction || 0)
        this.setStyle(fraction, { left: 5 })
        this.mapGame.appendChild(historyFraction)
        this.mapGame.appendChild(fraction)
    }

    // 【工具函数】
    // 创建 dom
    createDom(style) {
        const options = {
            width: 1,
            height: 1,
            zIndex: '100',
            borderRadius: '50%',
            position: 'absolute',
            backgroundColor: this.randomColor(),
        }
        let dom = document.createElement('div')
        return this.setStyle(dom, Object.assign({}, options, style))
    }
    // 随机生成颜色
    randomColor() {
        const N = () => ~~(Math.random() * 200 + 50)
        return `rgb(${N()},${N()},${N()})`
    }
    // 设置 dom 样式
    setStyle(dom, style) {
        const address = ['width', 'height', 'top', 'left']
        for (let key in style) {
            dom.style[key] = address.includes(key) ? style[key] * this.step + 'px' : style[key]
        }
        return dom
    }
    // 获取 dom 位置
    getDomAddr(dom) {
        let { top, left } = dom.style
        return { top: parseFloat(top) / this.step, left: parseFloat(left) / this.step }
    }
    // 渲染角色信息
    renderBody() {
        // 身体
        this.snakeBody.forEach(item => {
            this.mapGame.appendChild(item.dom)
        })
        // 分数
        let fraction = document.getElementById('fraction' + this.id)
        fraction.innerText = '分数：' + this.fraction
    }
}

const eatSnake = new Snake();